/* Uart Events Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/uart.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "spp.h"
#include "slave.h"
#include "rs485.h"

/**
 * This is a example which echos any data it receives on UART back to the sender using RS485 interface in half duplex mode.
*/
#define TAG "RS485"

extern uint16_t rs485_baud;


// Note: Some pins on target chip cannot be assigned for UART communication.
// Please refer to documentation for selected board and target to configure pins using Kconfig.
#define ECHO_TEST_TXD   (33)
#define ECHO_TEST_RXD   (32)

// RTS for RS485 Half-Duplex Mode manages DE/~RE
#define ECHO_TEST_RTS   (14)

// CTS is not used in RS485 Half-Duplex Mode
#define ECHO_TEST_CTS   (UART_PIN_NO_CHANGE)

#define BUF_SIZE        (1024)
#define BAUD_RATE       (rs485_baud * 100)  //(9600)

// Read packet timeout
#define PACKET_READ_TICS        (100 / portTICK_PERIOD_MS)
#define ECHO_TASK_STACK_SIZE    (2048)
#define ECHO_TASK_PRIO          (10)
#define ECHO_UART_PORT          (2)

// Timeout threshold for UART = number of symbols (~10 tics) with unchanged state on receive pin
#define ECHO_READ_TOUT          (3) // 3.5T * 8 = 28 ticks, TOUT=3 -> ~24..33 ticks

// 队列
static QueueHandle_t uart_send_queue;

// 待发送数据队列内数据结构
typedef struct 
{
    uint8_t *data;
    uint16_t length;
}rs485_send_data_t;

// rs485模式: 0: 透传模式, 1: 设置模式
static uint8_t u8rs485Mode = 0; 

/**
 * @brief 设置RS485模式
 * @param mode 0: 透传模式, 1: 设置模式
 */
void rs485_set_mode(uint8_t mode)
{
    u8rs485Mode = (mode == 0 ? 0 : 1);
}

static void echo_send(const int port, const char* str, uint8_t length)
{
    if(uart_write_bytes(port, str, length) != length) 
    {
        // add your code to handle sending failure here
        //abort();
        ESP_LOGE(TAG, "uart_write_bytes failed");
    }
}

// An example of echo test with hardware flow control on UART
static void echo_task(void *arg)
{
    // Allocate buffers for UART
    uint8_t* data = (uint8_t*) pvPortMalloc(BUF_SIZE);

    if(data == NULL) 
    {
        ESP_LOGE(TAG, "echo_task data malloc failed");
        return;
    }

    while(1) 
    {
        //Read data from UART
        int len = uart_read_bytes(ECHO_UART_PORT, data, BUF_SIZE, PACKET_READ_TICS);
        //Write data back to UART
        if(len > 0) 
        {
            //echo_send(uart_num, (const char*)data, len);
            //ESP_ERROR_CHECK(uart_wait_tx_done(uart_num, 10));
            //蓝牙发送数据
            if(u8rs485Mode == 0)
            {
                spp_send_data(data, len);
            }
            else
            {
                ESP_LOGD(TAG, "rs485 rsv len:%d", len);
                slave_send_data(data, len, rs485_send_data);
            }
        }
    }
    if(data)
    {
        vPortFree(data);
    }
    vTaskDelete(NULL);
}

static void rs485_send_task(void *arg)
{
    rs485_send_data_t send_data;

    // Allocate buffers for UART
    uint8_t* data = (uint8_t*)pvPortMalloc(BUF_SIZE);

    // 初始化队列
    uart_send_queue = xQueueCreate(4, sizeof(rs485_send_data_t));

    if(!uart_send_queue || !data) 
    {
        ESP_LOGE(TAG, "rs485_send_task queue or data malloc failed");
        return;
    }

    while(1) 
    {
        if(pdTRUE == xQueueReceive(uart_send_queue, &send_data, portMAX_DELAY)) 
        {
            if((send_data.length <= BUF_SIZE && send_data.length > 0) && send_data.data)
            {
                memcpy(data, send_data.data, send_data.length);
                echo_send(ECHO_UART_PORT, (const char*)data, send_data.length);
                vPortFree(send_data.data);
                ESP_ERROR_CHECK(uart_wait_tx_done(ECHO_UART_PORT, 2000 / portTICK_PERIOD_MS));
            }
            else
            {
                ESP_LOGE(TAG, "rs485_send_task data length error");
            }
        } 
    }
    if(data)
    {
        vPortFree(data);
    }
    if(uart_send_queue)
    {
        vQueueDelete(uart_send_queue);
        uart_send_queue = NULL;
    }
    vTaskDelete(NULL);
}

void rs485_send_data(uint8_t* data, uint16_t length)
{
    if(length > BUF_SIZE || length == 0) 
    {
        return;
    }
    if(uart_send_queue == NULL) 
    {
        return;
    }
    rs485_send_data_t send_data;
    uint8_t* send_data_buf = (uint8_t*) pvPortMalloc(length);
    if(send_data_buf == NULL) 
    {
        return;
    }
    send_data.data = send_data_buf;
    send_data.length = length;
    memcpy(send_data.data, data, length);
    if(xQueueSend(uart_send_queue, &send_data, 0) != pdTRUE) 
    {
        ESP_LOGE(TAG, "rs485_send_data queue full");
        vPortFree(send_data_buf);
    }
}


void rs485_app_main(void)
{
    uart_config_t uart_config = {
        .baud_rate = BAUD_RATE,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .rx_flow_ctrl_thresh = 122,
        .source_clk = UART_SCLK_DEFAULT,
    };

    // Set UART log level
    esp_log_level_set(TAG, ESP_LOG_INFO);

    // Install UART driver (we don't need an event queue here)
    // In this example we don't even use a buffer for sending data.
    ESP_ERROR_CHECK(uart_driver_install(ECHO_UART_PORT, BUF_SIZE * 2, 0, 0, NULL, 0));

    // Configure UART parameters
    ESP_ERROR_CHECK(uart_param_config(ECHO_UART_PORT, &uart_config));

    // Set UART pins as per KConfig settings
    ESP_ERROR_CHECK(uart_set_pin(ECHO_UART_PORT, ECHO_TEST_TXD, ECHO_TEST_RXD, ECHO_TEST_RTS, ECHO_TEST_CTS));

    // Set RS485 half duplex mode
    ESP_ERROR_CHECK(uart_set_mode(ECHO_UART_PORT, UART_MODE_RS485_HALF_DUPLEX));

    // Set read timeout of UART TOUT feature
    ESP_ERROR_CHECK(uart_set_rx_timeout(ECHO_UART_PORT, ECHO_READ_TOUT));

    // Start echo task
    xTaskCreate(echo_task, "uart_task", ECHO_TASK_STACK_SIZE, NULL, ECHO_TASK_PRIO, NULL);

    // Start rs485 send task
    xTaskCreate(rs485_send_task, "rs485_task", ECHO_TASK_STACK_SIZE, NULL, ECHO_TASK_PRIO, NULL);
}
